/***********************************************************************************************************************
*  OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC.
*  See also https://openstudio.net/license
***********************************************************************************************************************/

#include "RefrigerationSubcoolerMechanical.hpp"
#include "RefrigerationSubcoolerMechanical_Impl.hpp"

#include "RefrigerationSystem.hpp"
#include "RefrigerationSystem_Impl.hpp"
#include "Model.hpp"
#include "Model_Impl.hpp"

#include <utilities/idd/OS_Refrigeration_Subcooler_Mechanical_FieldEnums.hxx>
#include <utilities/idd/IddEnums.hxx>

#include "../utilities/units/Unit.hpp"

#include "../utilities/core/Assert.hpp"

namespace openstudio {
namespace model {

  namespace detail {

    RefrigerationSubcoolerMechanical_Impl::RefrigerationSubcoolerMechanical_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle)
      : ModelObject_Impl(idfObject, model, keepHandle) {
      OS_ASSERT(idfObject.iddObject().type() == RefrigerationSubcoolerMechanical::iddObjectType());
    }

    RefrigerationSubcoolerMechanical_Impl::RefrigerationSubcoolerMechanical_Impl(const openstudio::detail::WorkspaceObject_Impl& other,
                                                                                 Model_Impl* model, bool keepHandle)
      : ModelObject_Impl(other, model, keepHandle) {
      OS_ASSERT(other.iddObject().type() == RefrigerationSubcoolerMechanical::iddObjectType());
    }

    RefrigerationSubcoolerMechanical_Impl::RefrigerationSubcoolerMechanical_Impl(const RefrigerationSubcoolerMechanical_Impl& other,
                                                                                 Model_Impl* model, bool keepHandle)
      : ModelObject_Impl(other, model, keepHandle) {}

    const std::vector<std::string>& RefrigerationSubcoolerMechanical_Impl::outputVariableNames() const {
      static const std::vector<std::string> result{// TODO: implement checks
                                                   // FOR SUBCOOLERS ON SYSTEMS SERVING CASES AND/OR WALKINS:
                                                   "Refrigeration System Mechanical Subcooler Heat Transfer Rate",
                                                   "Refrigeration System Mechanical Subcooler Heat Transfer Energy",
                                                   // FOR SUBCOOLERS ON SYSTEMS SERVING AIR CHILLERS:
                                                   "Refrigeration Air Chiller System Mechanical Subcooler Heat Transfer Rate",
                                                   "Refrigeration Air Chiller System Mechanical Subcooler Heat Transfer Energy"};
      return result;
    }

    IddObjectType RefrigerationSubcoolerMechanical_Impl::iddObjectType() const {
      return RefrigerationSubcoolerMechanical::iddObjectType();
    }

    ModelObject RefrigerationSubcoolerMechanical_Impl::clone(Model model) const {
      auto modelObjectClone = ModelObject_Impl::clone(model).cast<RefrigerationSubcoolerMechanical>();

      modelObjectClone.resetCapacityProvidingSystem();

      return std::move(modelObjectClone);
    }

    boost::optional<RefrigerationSystem> RefrigerationSubcoolerMechanical_Impl::capacityProvidingSystem() const {
      return getObject<ModelObject>().getModelObjectTarget<RefrigerationSystem>(OS_Refrigeration_Subcooler_MechanicalFields::CapacityProvidingSystem);
    }

    boost::optional<double> RefrigerationSubcoolerMechanical_Impl::outletControlTemperature() const {
      return getDouble(OS_Refrigeration_Subcooler_MechanicalFields::OutletControlTemperature, true);
    }

    bool RefrigerationSubcoolerMechanical_Impl::setCapacityProvidingSystem(const boost::optional<RefrigerationSystem>& refrigerationSystem) {
      bool result(false);
      if (refrigerationSystem) {
        result = setPointer(OS_Refrigeration_Subcooler_MechanicalFields::CapacityProvidingSystem, refrigerationSystem.get().handle());
      } else {
        resetCapacityProvidingSystem();
        result = true;
      }
      return result;
    }

    void RefrigerationSubcoolerMechanical_Impl::resetCapacityProvidingSystem() {
      bool result = setString(OS_Refrigeration_Subcooler_MechanicalFields::CapacityProvidingSystem, "");
      OS_ASSERT(result);
    }

    bool RefrigerationSubcoolerMechanical_Impl::setOutletControlTemperature(boost::optional<double> outletControlTemperature) {
      bool result(false);
      if (outletControlTemperature) {
        result = setDouble(OS_Refrigeration_Subcooler_MechanicalFields::OutletControlTemperature, outletControlTemperature.get());
      } else {
        resetOutletControlTemperature();
        result = true;
      }
      OS_ASSERT(result);
      return result;
    }

    void RefrigerationSubcoolerMechanical_Impl::resetOutletControlTemperature() {
      bool result = setString(OS_Refrigeration_Subcooler_MechanicalFields::OutletControlTemperature, "");
      OS_ASSERT(result);
    }

    boost::optional<RefrigerationSystem> RefrigerationSubcoolerMechanical_Impl::system() const {

      boost::optional<RefrigerationSystem> system;
      // We use getModelObjectSources to check if more than one
      std::vector<RefrigerationSystem> systems =
        getObject<ModelObject>().getModelObjectSources<RefrigerationSystem>(RefrigerationSystem::iddObjectType());

      if (!systems.empty()) {
        if (systems.size() > 1u) {
          LOG(Error, briefDescription() << " is referenced by more than one RefrigerationSystem, returning the first");
        }
        system = systems[0];
      }
      return system;
    }

  }  // namespace detail

  RefrigerationSubcoolerMechanical::RefrigerationSubcoolerMechanical(const Model& model)
    : ModelObject(RefrigerationSubcoolerMechanical::iddObjectType(), model) {
    OS_ASSERT(getImpl<detail::RefrigerationSubcoolerMechanical_Impl>());

    setOutletControlTemperature(10.0);
  }

  IddObjectType RefrigerationSubcoolerMechanical::iddObjectType() {
    return {IddObjectType::OS_Refrigeration_Subcooler_Mechanical};
  }

  boost::optional<RefrigerationSystem> RefrigerationSubcoolerMechanical::capacityProvidingSystem() const {
    return getImpl<detail::RefrigerationSubcoolerMechanical_Impl>()->capacityProvidingSystem();
  }

  boost::optional<double> RefrigerationSubcoolerMechanical::outletControlTemperature() const {
    return getImpl<detail::RefrigerationSubcoolerMechanical_Impl>()->outletControlTemperature();
  }

  bool RefrigerationSubcoolerMechanical::setCapacityProvidingSystem(const RefrigerationSystem& refrigerationSystem) {
    return getImpl<detail::RefrigerationSubcoolerMechanical_Impl>()->setCapacityProvidingSystem(refrigerationSystem);
  }

  void RefrigerationSubcoolerMechanical::resetCapacityProvidingSystem() {
    getImpl<detail::RefrigerationSubcoolerMechanical_Impl>()->resetCapacityProvidingSystem();
  }

  bool RefrigerationSubcoolerMechanical::setOutletControlTemperature(double outletControlTemperature) {
    return getImpl<detail::RefrigerationSubcoolerMechanical_Impl>()->setOutletControlTemperature(outletControlTemperature);
  }

  void RefrigerationSubcoolerMechanical::resetOutletControlTemperature() {
    getImpl<detail::RefrigerationSubcoolerMechanical_Impl>()->resetOutletControlTemperature();
  }

  boost::optional<RefrigerationSystem> RefrigerationSubcoolerMechanical::system() const {
    return getImpl<detail::RefrigerationSubcoolerMechanical_Impl>()->system();
  }

  /// @cond
  RefrigerationSubcoolerMechanical::RefrigerationSubcoolerMechanical(std::shared_ptr<detail::RefrigerationSubcoolerMechanical_Impl> impl)
    : ModelObject(std::move(impl)) {}
  /// @endcond

}  // namespace model
}  // namespace openstudio
